#!/bin/bash

# Copyright 2021, soneca e cv8minix3.
#
# soneca acenos@danwin1210.me 
# cv8minix3 terroristcv8@keemail.me
# 
# album-dl-advanced é um software livre; você pode redistribuí-lo e/ou
# modificá-lo sob os termos da Licença Pública Geral GNU como publicada
# pela Free Software Foundation; na versão 3 da Licença, ou
# (a seu critério) qualquer versão posterior.
#
# Este programa é distribuído na esperança de que possa ser útil,
# mas SEM NENHUMA GARANTIA; sem uma garantia implícita de ADEQUAÇÃO
# a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a
# Licença Pública Geral GNU para mais detalhes.
#
# Você deve ter recebido uma cópia da Licença Pública Geral GNU junto
# com este programa. Se não, veja <http://www.gnu.org/licenses/>.

## Formatação de saída
INTE="[\e[33mINTE\e[0m]"
INFO="[\e[32mINFO\e[0m]"
ERRO="[\e[31mERRO\e[0m]"

## Variáveis úteis
ua="Mozilla/5.0 (Android 7.0; Mobile; rv:54.0) Gecko/54.0 Firefox/54.0"
headers='--header="Accept-Language: pt-BR,pt" --header="Accept-Encoding: identity"'


## Imprime em tela os padrões de uso.
function USO(){
	echo -e "Uso: $0 \e[92m[AÇÃO]\e[0m \e[33m[OPÇÃO]\e[0m"
	echo -e "\t\e[92m[-d|--dl|--download]\e[0m \e[33m[URL_PLAYLIST]\e[0m	:\e[94mBaixa álbum.\e[0m"
	echo -e "\t\e[92m[-f|--fix|--fixname]\e[0m \e[33m[STRING]\e[0m		:\e[94mPadroniza nomes dos arquivos e remove string especificada.\e[0m"
	echo -e "\t\e[92m[-s|--set|--set-tags]\e[0m \e[33m[URL_METADADOS]\e[0m	:\e[94mInsere metadados nos arquivos.\e[0m"
	echo -e "\t\e[92m[-v|--show|--show-tags]\e[0m			:\e[94mMostra metadados dos arquivos.\e[0m"
	exit 1;	
}

# Checa dependências para execução do script.
function DEP_CHECK()
{
	# Todos os comandos externos de que o script depende.
	local IFS=' ' cmd libs="id3v2 youtube-dl ffmpeg sed egrep wget uniq ls mv"
	for cmd in $libs; do
		type -tf $cmd &> /dev/null; test $? -ne 0 && echo -e "$ERRO \"$cmd\" não instalado!" && exit 2
	done
}

## Substitui espaços e remove caractere estranhos
function FIXNAME(){
	num=1;
	while true; do
		ls ${num}-* 1>/dev/null 2>/dev/null
		if [ $? -eq 0 ]; then
			file=$(ls ${num}-* 2>/dev/null)
			
			# Remover espaços
			newname=${file// /_}

			# Remover caracteres bagunceiros
			newname=${newname//[\!\@\#\$\%\&\*\?\;\(\)\[\]\]\{\}\<\>\'\"\`\\]/}

			# Remover padrão especificado em $2 ex.: album-dl --fixname "_Áudio_Oficial"
			[[ ! -z $1 ]] && newname=${newname//$1/};

			[[ "$file" != "$newname" ]] && mv "$file" "$newname" && echo "$newname";
		else
			# Sem mais arquivos
			break
		fi
		let num++
	done
}

## Captura metadados do www.letras.mus.br
function GET_METADATA(){

	# Para padronizar em qualquer ambiente.
	local link_tmp="$1" link="$1" IFS=$'\n' n=0 tmp i dados_html="" retorno=2
	local error_msg="Passar link do albúm https://www.letras.mus.br/[Artista]/discografia/[Nome do albúm]/"

	# Deve ser um link do www.letras.mus.br.
	if [ -z "$link_tmp" ]; then
		echo "$error_msg"
		return 2
	fi
	
	# Filtrando domínio para conferir.
	link_tmp=${link_tmp#*w}; link_tmp="w$link_tmp";	link_tmp=${link_tmp%%/*}

	# Testa.
	if [ "$link_tmp" != "www.letras.mus.br" ]; then
		echo "$error_msg"
		return 2
	fi

	# Obtém os metadados brutos.
	while [ "$retorno" -ne "0" ]; do
		dados_html=$( wget -q --user-agent="$ua" "$headers" $link -O - )
		retorno=$?
	done

	# Nome do artista/banda.
	artista=${dados_html#*Discografia de }; artista=${artista%% - LETRAS.MUS.BR*}

	# Nome do albúm.
	album=${dados_html#*\<title\>}; album=${album%% |*}

	# O ano vem na url.
	ano=${link##*-}; ano=${ano/\//}

	# Gênero.
	genero=${dados_html##*\"genre\":\"}; genero=${genero%%\",*}

	# Nomes das músicas.
	dados_musicas=$( echo "$dados_html" | sed 's/\<li\>/\n/g' | egrep ' data-name=' )

	for i in $dados_musicas; do	
		i=${i##*\<b\>}; i=${i%%\</b\>*}; let n++
		if [ -z "$tmp" ]; then
			tmp="$n - $i"
		else
			tmp="$tmp\n$n - $i"
		fi
	done
	dados_musicas="$( echo -e $tmp )"
}
## Insere metadados nos arquivos de música
function SET_TAGS(){
	## Verifica se tem o id3v2 (editor tags id3)
	id3v2 --help 1>/dev/null 2>/dev/null
	[[ $? -ne 0 ]] && echo "id3v2 não encontrado" && exit 1

	## Recebe link do letras.mus.br
	[[ -z "$url_letras" ]] && echo -en "$INTE Link do álbum(pode deixar vazio): " && read	url_letras
	[[ ! -z "$url_letras" ]] && GET_METADATA $url_letras && echo -e "$INFO Gênero registrado : \e[1;7;94m$dados_genero\e[0m"

	## Recebe metadados que ainda não foram definidos
	[[ -z "$artista" ]] && read -p "Artista: " artista
	[[ -z "$album" ]] && read -p "Album: " album
	[[ -z "$ano" ]] && read -p "Ano: " ano
	generos=("Blues" "Classic rock" "Country" "Dance" "Disco" "Funk" "Grunge" "Hip-Hop" "Jazz" "Metal" "New Age" "Oldies" "Other" "Pop" "Rhythm and Blues" "Rap" "Reggae" "Rock" "Techno" "Industrial" "Alternative" "Ska" "Death metal" "Pranks" "Soundtrack" "Euro-Techno" "Ambient" "Trip-Hop" "Vocal" "Jazz & Funk" "Fusion" "Trance" "Classical" "Instrumental" "Acid" "House"	"Game" "Sound clip" "Gospel" "Noise" "Alternative Rock" "Bass" "Soul" "Punk" "Space" "Meditative" "Instrumental Pop" "Instrumental Rock" "Ethnic" "Gothic" "Darkwave" "Techno-Industrial" "Electronic" "Pop-Folk" "Eurodance" "Dream" "Southern Rock" "Comedy" "Cult" "Gangsta" "Top 40" "Christian Rap" "Pop/Funk" "Jungle" "Native US" "Cabaret" "New Wave" "Psyche	 delic" "Rave" "Show tunes" "Trailer" "Lo-Fi" "Tribal" "Acid Punk" "Acid Jazz" "Polka" "Retro" "Musical" "Rock ’n’ Roll" "Hard rock")
	select genero in "${generos[@]}"; do
		case $genero in
			"Blues") genero=0; break;;
			"Classic rock") genero=1; break;;
			"Country") genero=2; break;;
			"Dance") genero=3; break;;
			"Disco") genero=4; break;;
			"Funk") genero=5; break;;
			"Grunge") genero=6; break;;
			"Hip-Hop") genero=7; break;;
			"Jazz") genero=8; break;;
			"Metal") genero=9; break;;
			"New Age") genero=10; break;;
			"Oldies") genero=11; break;;
			"Other") genero=12; break;;
			"Pop") genero=13; break;;
			"Rhythm and Blues") genero=14; break;;
			"Rap") genero=15; break;;
			"Reggae") genero=16; break;;
			"Rock") genero=17; break;;
			"Techno") genero=18; break;;
			"Industrial") genero=19; break;;
			"Alternative") genero=20; break;;
			"Ska") genero=21; break;;
			"Death metal") genero=22; break;;
			"Pranks") genero=23; break;;
			"Soundtrack") genero=24; break;;
			"Euro-Techno") genero=25; break;;
			"Ambient") genero=26; break;;
			"Trip-Hop") genero=27; break;;
			"Vocal") genero=28; break;;
			"Jazz & Funk") genero=29; break;;
			"Fusion") genero=30; break;;
			"Trance") genero=31; break;;
			"Classical") genero=32; break;;
			"Instrumental") genero=33; break;;
			"Acid") genero=34; break;;
			"House") genero=35; break;;
			"Game") genero=36; break;;
			"Sound clip") genero=37; break;;
			"Gospel") genero=38; break;;
			"Noise") genero=39; break;;
			"Alternative Rock") genero=40; break;;
			"Bass") genero=41; break;;
			"Soul") genero=42; break;;
			"Punk") genero=43; break;;
			"Space") genero=44; break;;
			"Meditative") genero=45; break;;
			"Instrumental Pop") genero=46; break;;
			"Instrumental Rock") genero=47; break;;
			"Ethnic") genero=48; break;;
			"Gothic") genero=49; break;;
			"Darkwave") genero=50; break;;
			"Techno-Industrial") genero=51; break;;
			"Electronic") genero=52; break;;
			"Pop-Folk") genero=53; break;;
			"Eurodance") genero=54; break;;
			"Dream") genero=55; break;;
			"Southern Rock") genero=56; break;;
			"Comedy") genero=57; break;;
			"Cult") genero=58; break;;
			"Gangsta") genero=59; break;;
			"Top 40") genero=60; break;;
			"Christian Rap") genero=61; break;;
			"Pop/Funk") genero=62; break;;
			"Jungle") genero=63; break;;
			"Native US") genero=64; break;;
			"Cabaret") genero=65; break;;
			"New Wave") genero=66; break;;
			"Psychedelic") genero=67; break;;
			"Rave") genero=68; break;;
			"Show tunes") genero=69; break;;
			"Trailer") genero=70; break;;
			"Lo-Fi") genero=71; break;;
			"Tribal") genero=72; break;;
			"Acid Punk") genero=73; break;;
			"Acid Jazz") genero=74; break;;
			"Polka") genero=75; break;;
			"Retro") genero=76; break;;
			"Musical") genero=77; break;;
			"Rock ’n’ Roll") genero=78; break;;
			"Hard rock") genero=79; break;;
			*) echo "ERRO" ;;
		esac
	done

	## Imprime informações a etiquetar
	files=$(ls *mp3 2>/dev/null)
	local IFS=$'\n'
	for s in ${files[@]}; do
		# Captura Número da Faixa ex.: './8_-_Holy_Mountains.mp3'
		faixa=$(echo ${s} | sed 's/\.\///g' | egrep -o '^[1234567890]*' )

		# Captura Título da Música
		titulo=$(echo ${s} | sed 's/\.\///g' | sed 's/^[1234567890]*_*-_*//g' | sed 's/\.mp3//g' |sed 's/_/ /g')

		echo -e "$INFO Artista: \e[91m$artista\e[0m | Álbum: \e[96m$album\e[0m | Ano: \e[33m$ano\e[0m | " \
				"Gênero: \e[94m${generos[${genero}]}\e[0m | Título: \e[92m$titulo\e[0m | Arquivo: \e[93m$s\e[0m"
	done

	## Aplica etiquetas
	read -p "Aplicar? (Sim/Não): " op
	if [[ "$op" == "Sim" || "$op" == "sim" || "$op" == "S" || "$op" == "s" ]];then
		for s in ${files[@]}; do
			titulo=$(echo ${s} | sed 's/\.\///g' | sed 's/^[1234567890]*_*-_*//g' | sed 's/\.mp3//g' |sed 's/_/ /g')
			faixa=$(echo ${s} | sed 's/\.\///g' | egrep -o '^[1234567890]*' )
			[[ ! -z "$titulo" ]] && id3v2 -t "$titulo" $s;
			[[ ! -z "$artista" ]] && id3v2 -a "$artista" $s;
			[[ ! -z "$album" ]] && id3v2 -A "$album" $s;
			[[ ! -z "$genero" ]] && id3v2 -g "$genero" $s;
			[[ ! -z "$faixa" ]] && id3v2 -T "$faixa" $s;
			[[ ! -z "$ano" ]] && id3v2 -y "$ano" $s;
		done
	fi
}

## Mostra metadados nos arquivos de música
function SHOW_TAGS(){
	## Verifica se tem o id3v2 (editor tags id3)
	exiftool --help 1>/dev/null 2>/dev/null
	[[ $? -ne 0 ]] && echo -e "$ERRO exiftool não encontrado" && exit 1

	## Imprime informações a etiquetar
	files=$(ls *mp3 2>/dev/null)
	local IFS=$'\n'
	for s in ${files[@]}; do
		## id3v2 Buga com letras acentuadas
		res=$(exiftool $s) 
		local artista=$(echo "$res" | egrep ^Artist | sed "s/Artist *: *//g")
		local album=$(echo "$res" | egrep ^Album | sed "s/Album *: *//g")
		local ano=$(echo "$res" | egrep ^Year | sed "s/Year *: *//g")
		local genero=$(echo "$res" | egrep ^Genre | sed "s/Genre *: *//g")
		local titulo=$(echo "$res" | egrep ^Title | sed "s/Title *: *//g")

		echo -e "$INFO Artista: \e[91m$artista\e[0m | Álbum: \e[96m$album\e[0m | Ano: \e[33m$ano\e[0m | " \
				"Gênero: \e[94m$genero\e[0m | Título: \e[92m$titulo\e[0m | Arquivo: \e[93m$s\e[0m"
	done
}

# Baixar album
function DL(){
	## Recebe link da playlist
	if [[ -z "$1" ]];then
		echo -ne "$INTE Endereço da playlist: " && read url;
	else
		url=$1
	fi
	## Recebe Instâncias Invidious
	instancias=$(
		wget -qO- "https://api.invidious.io/instances.json?pretty=1&sort_by=health" | \
		egrep -v "\.onion|\.i2p" | egrep -o "https*://[^/\",]*"
	)
	[[ -z "$instancias" ]] && echo -e "$ERRO Erro no recebimento de instâncias" && exit 1

	## Captura de links das músicas a partir da playlist
	links=$( wget --no-check-certificate "$url" -qO- | egrep -o 'watch\?v=[^\"\&]*' | uniq );
	[[ -z "$links" ]] && echo -e "$ERRO Erro no recebimento da playlist" && exit 1

	## Prioriza o domínio da playlist entre as instâncias
	instancias=${instancias/${url%/*}$'\n'/}; instancias=${url%/*}$'\n'$instancias
	local instancia_on=''

	num=1;
	for song in ${links[@]}; do
		local arq=$num-*.mp3; test $arq != $num'-*.mp3' && \
		echo -e "$INFO O arquivo parece já ter sido baixado: \e[93m$(ls ${num}-*.mp3)\e[0m" && let num++ && continue

		for instancia in $instancia_on ${instancias[@]}; do
			echo -e "$INFO Executando: \e[93myoutube-dl -x --audio-format \"mp3\" "\
					"-o \"${num}-%(title)s.%(ext)s\" \"\e[36m${instancia}/${song}&listen=1\e[93m\"\e[0m"
			youtube-dl -x --audio-format "mp3" -o "${num}-%(title)s.%(ext)s" "${instancia}/${song}&listen=1"

			if [ $? -eq 0 ];then
				instancia_on=$instancia
				break
			else
				test "$instancia" = "$instancia_on" && instancia_on=''
				echo -e "$ERRO O youtube-dl falhou"
				continue
			fi
		done

		## Depois de tentar todas as instâncias verifica se o arquivo foi baixado
		if [ ! -z "$(ls ${num}-* 2>/dev/null)" ];then
			#Sucesso
			echo -e "$INFO Arquivo salvo: \e[95m$(ls $num-* 2>/dev/null)\e[0m"
		else
			#Falha
			echo -e "$ERRO Instâncias esgotadas! Verifique a sua conexão" && exit 1
		fi
		let num++
	done
}

## Checa dependências a cada execução.
DEP_CHECK

# Verica por comandos aceitos, início.
case "$1" in
	'--dl'|'--download'|'-d') DL $2 ;;
	'--fix'|'--fixname'|'-f') FIXNAME $2 ;;
	'--set'|'--set-tags'|'-s') SET_TAGS ;;
	'--sh'|'--show-tags'|'-v') SHOW_TAGS ;;
							*) USO ;;
esac
